Graphorall

Back

    OS case study

    • 概述及发展历史

      • OpenHarmony OS(以下简称OHOS)是由开放原子开源基金会孵化及运营的开源项目,目标是面向全场景、全连接、全智能时代,基于开源的方式,搭建一个智能终端设备操作系统的框架和平台,促进万物互联产业的繁荣发展。

        • OHOS项目于2020年9月10日开源发布第一个版本OHOS 1.0 release,支持轻型及小型系统,其所依赖的LiteOS-M内核源于2015年发布的LiteOS操作系统项目。LiteOS源于2012年华为为其多种产品开发的嵌入式系统,后演进为开源项目,目前演进到了v5.0。而LiteOS-A内核则是在LiteOS-M基础上增加MMU、内核隔离、内核模块框架、IPC等功能而来。2021年6月发布了OHOS 2.0 Canary版本,主要增加标准系统即Linux内核支持。2021年9月发布3.0 LTS版本,对系统服务层各子系统尤其是分布式能力进行了增强。最新的版本发布为2022年9月30日发布的3.2 Beta3。作为现代操作系统,其包含了大量为上层应用提供的系统服务与框架,因此该部分的更新较为频繁。

          • OHOS遵循分层设计,分为内核层、系统服务层、框架层和应用层;系统功能按照“系统 > 子系统 > 组件”逐级展开,实现了子系统及组件级的可插拔、可裁剪。下图为其整体架构:

            • image.png

              • 如图所示,内核层由内核子系统与驱动子系统组成。内核子系统由下层支持的多种内核与OHOS的KAL(内核抽象层)组成,实现了多内核差异的消除,对上层提供统一的进程/线程、内存管理、文件系统、网络协议服务和POSIX接口。驱动子系统基于HDF(Hardware Driver Framework)对上提供统一的外设访问能力,对下规定多内核统一的硬件驱动接口。其目前支持的内核有:Liteos-m,面向轻量系统(mini)即传统嵌入式开发领域;Liteos-a,面向小型系统(small)即拥有多媒体能力的智能硬件;Linux kernel,面向标准系统即采用Cortex-A级以上处理器的设备。

                • 系统服务层由系统基本能力、基础软件服务、增强软件服务、硬件服务子系统集组成。其中系统基本能力子系统集主要提供分布式应用所需服务,也是OHOS作为分布式基座的关键所在;基础软件与增强软件子系统集主要提供上层应用所需的系统服务,与Linux中的D-Bus远程调用框架、ALSA音频框架等系统中间件类似;硬件服务子系统集主要提供嵌入式传感器的数据记录处理、传感器调用等功能,是OHOS作为物联设备操作系统的关键所在。

                  • 不同内核(系统类型)在系统服务层的区别主要在于,轻型系统不支持进程隔离,因此其系统服务与内核、应用运行于同一空间,基于MPU(内存保护单元)隔离;而小型及标准系统的系统服务为一个个独立的进程,通过IPC调用。

                    • 考虑到基础功能的丰富程度与重叠,我们的分析主要集中在内核层,以Liteos-a型内核为例。

                        • 上图为LiteOS-A内核(后文简称为LOS内核)架构图,其中基础内核主要包括内核的基础机制,如调度、内存管理、中断异常等;扩展组件主要包括文件系统、网络协议和安全等扩展功能。以下分别对进程管理、内存管理、设备驱动、文件系统进行详细分析。

                        • 进程及线程

                          • LOS中进程及线程为两个解耦的概念。进程是系统资源管理的最小单元,主要用于实现用户态进程的隔离。任务是竞争系统资源的最小运行单元,LOS内核中一个任务表示一个线程。可见,LOS内核中进程只是资源管理单元,实际运行是由绑定到进程的各个任务(即线程)完成的。

                            • 内核进程 内核态被视为同一个进程空间,其中运行有内核进程KProcess(pid=2),以及系统提供的空闲进程KIdle(pid=0)。fixed pids在SMP多处理器设备上,启动其他物理核心时内核为每个物理核心都分配了一个KIdle线程,当核心空闲时,其被调度到核心上循环执行WaitForInterrupt指令以减少能耗。kidle source

                              • 用户进程 用户态init进程userInitProcess(pid=1)是第一个用户态进程,由内核创建并载入init程序,其他用户态进程均由该进程fork而来,因此其为用户态进程树的根。用户态进程通过fork父进程创建,fork进程时会将父进程的进程虚拟内存空间clone到子进程,子进程实际运行时通过写时复制机制将父进程的内容按需复制到子进程的虚拟内存空间。

                                • 进程与任务调度 与主流操作系统类似,LOS中任务调度模型有如下五种状态: image.png 相对应的,进程也有五种状态: image.png 与linux等类似,当一个进程的所有线程都处于阻塞状态,该进程处于pending态;只要有其中一个线程处于running态,进程多外展示为running态,虽然其所属线程可能仍在ready队列中。 任务调度采用高优先级优先(HPF, Highest Priority First)+同优先级时间片轮转(RR)的抢占式调度算法。 优先级又分为进程优先级与线程优先级,均为32个优先级(0-31)。其中用户进程可配置的优先级有22个(10-31),最高优先级为10,最低优先级为31。调度时首先考虑进程优先级,高优先级的进程可抢占低优先级进程,低优先级进程必须在高优先级进程阻塞或结束后才能得到调度;同优先级进程中的所有线程也按优先级执行上述规则;最终进程优先级与线程优先级均相同的任务,执行时间片轮转调度策略。下图所示即为任务调度框架中按优先级索引任务控制块(TCB)的两级优先级队列: image.png 同时内核维护了g_priqueueBitmap用于记录哪些优先级队列为空,调度时使用位运算可在O(1)时间复杂度内获取最高优先级队列。 下图所示为任务调度器的调度流程: image.png

                                  • 内核通信机制 LOS支持事件、信号量、互斥锁、消息队列、读写锁、信号等内核通信机制。其中事件、信号量、消息队列用于(可以是不同进程)任务间通信与同步,互斥锁、读写锁等用于同一进程中不同任务间的同步,信号用于进程间通信。

                                  • 内存管理

                                    • LOS内核采用分页式内存管理。除了内核占用的一部分内存外,其余可用内存均以4KiB为单位划分成页帧。物理内存分配情况如图所示,内核占用的部分又分为内核镜像(kernel.bin)与内核堆(heap): image.png 虚拟内存分配与主流系统一致,内核态由低到高分别为DMA区、Normal区(内核数据段、代码段、内核堆、内核栈),用户态分别为数据与代码段、用户态堆、用户态栈、共享库映射区。

                                      • 内存分配 物理内存分配采用以页帧为基本单元的伙伴算法实现,此处不多赘述。 堆内存分配采用的TLSF(Two Level Segregate Fit)算法为LOS的一大亮点。堆内存分配在内核程序与用户程序中均有使用,是在分页基础上对进程获取到的大块虚拟内存空间(或内核堆空间)进一步细分的过程,对应于libc中的malloc等函数。该算法是一种用于实时操作系统的内存分配算法,用两级结构对空闲块按2次幂大小进行划分,减少内存的外部碎片;采用两级链表+bitmap索引的方式来加快查找,分配的复杂度为O(1),保证实时性。

                                        • 内存保护及映射 内存保护及映射依赖MMU完成(创建及修改通过软件完成,页表查找及鉴权通过MMU硬件完成),页表采用两级页表。 页表具体参数及PTE结构部分取决于目标设备架构的MMU定义,因此该部分参数宏定义在arch/路径下的架构相关代码里。以64位arm架构为例,其中一级页表每个PTE管理1MB空间的映射,所指向的二级页表每个表项映射一个虚拟页至物理帧,页表项PTE_T大小均为8B,因此每个L2页表共256项占2KB。 相关代码位于kernel_liteos_a/kernel/base/vm/路径下。

                                        • 设备驱动

                                          • OHOS采用多内核设计,支持系统在不同资源容量的设备部署,让设备驱动程序在不同内核间平滑迁移,消除驱动代码移植适配和维护的负担。因此这里所分析的LOS内核驱动框架实际上即是OHOS驱动子系统HDF框架。 HDF框架为驱动开发者提供驱动加载、驱动服务管理和驱动消息机制等功能。其架构如下图所示,其中可见为兼容多内核设计的基础内核兼容层OSAL、向上层系统服务提供的统一接口HDI image.png

                                            • 设备驱动过程 设备驱动可理解为一系列动态加载的系统服务。这类系统服务由HDF的服务管理器统一管理,不同的驱动服务由驱动服务名字符串唯一索引。调用方式上,可以通过DevSvcManagerClntGetService获取指定名称的驱动服务的函数指针;也可以通过HdfDeviceSubscribeService订阅指定名称的驱动服务,待该驱动初始化完成后自动调用传入的回调函数;还可以通过消息机制注册EventListener监听驱动产生的event,或主动发送消息给驱动。 内核启动后HDF框架根据HCS(HDF Configuration Source)配置中定义的优先级等属性按次序调用各个驱动的初始化函数,并在驱动服务管理器上注册驱动的各个service函数,完成后会调用驱动使用者订阅的回调函数。 具体的,HDF框架通过HdfDriverEntry结构体获得驱动的Init(初始化)、Bind(服务注册)、Release(驱动注销资源释放)等函数。初始化函数中一般进行外设寄存器配置、检测等操作,例如通过寄存器设置uart收发器的波特率。bind函数中一般进行所有服务的注册,而服务注册实际上就是存储一个结构体的地址,最后是靠调用方将其解析为某一特定类型的结构体,再从中获取函数指针。具体的服务,则可具有多种多样的输入参数,执行各种功能了;例如IO设备可能就有write(char)服务,内部维护一个缓冲区,当缓冲区满后向IO模块的寄存器写入其内容并清空。

                                              • 驱动框架 HDF之所以称为框架,除了其规定的HDI及服务管理等功能,还在于其提供的众多标准模型。其一是平台驱动层,面向最底层的软硬件接口,主要包括最常见的I2C/HDMI等标准总线、RTC/WatchDog等标准外设,只需实现框架预留的具体接口即可复用上层的设备管理及协议实现等。其二是外设驱动模型,面向上层多样化的器件功能,如相机、音频接口、生物识别装置等,提供典型的功能抽象模型,免于重复设计架构。

                                              • 文件系统

                                                • 与Linux等相似,LOS也采用VFS(虚拟文件系统)+具体文件系统的整体架构,以粘合下层异构的文件系统。目前其官方支持的文件系统有:FAT、JFFS2(Journalling Flash File System Version 2,日志文件系统)、NFS(网络文件系统)、RAMFS及Procfs(类Unix系统普遍使用的,通过文件形式访问进程信息的虚拟文件系统)。

                                                  • 虚拟文件系统 LOS的VFS层,主要通过函数指针实现对不同文件系统类型调用不同接口实现标准接口功能;通过Vnode与PathCache机制,提升路径搜索以及文件访问的性能;通过挂载点管理进行分区管理;通过FD(文件描述符)管理进行进程间FD隔离。 其中Vnode是具体文件或目录在VFS层的抽象封装,它屏蔽了不同文件系统的差异,实现资源的统一管理,主要有以下几种类型的节点:

                                                    • 挂载点:挂载具体文件系统,如/、/storage
                                                    • 设备节点:/dev目录下的节点,对应于一个设备,如/dev/mmcblk0
                                                    • 文件/目录节点:对应于具体文件系统中的文件/目录,如/bin/init 缓存具有VnodeCache、PathCache、PageCache三种。Vnode通过哈希以及LRU机制进行管理,较为简单。而PathCache则以父节点Vnode的地址和子节点的文件名做key、子节点Vnode作为value维护一张哈希表,可以从PathCache中快速查找到子节点对应的Vnode,同样采用LRU机制。PageCache目前仅支持缓存二进制文件,在初次访问文件时通过mmap映射到内存中,再访问时直接从PageCache中读取,同样采用LRU机制;另外基于PageCache可实现以文件为基底的进程间通信。 FD管理方面,进程只能访问本进程的fd,所有进程的fd映射到全局fd表中进行统一分配管理,系统支持的文件、消息队列、socket三类fd数量均有上限。 挂载点采用链表实现,全系统统一管理。 最后是VFS接口,文档中对所有api在各文件系统的支持度有总结,并支持通过POSIX接口调用。
                                                    • JFFS2文件系统 JFFS2是Journalling Flash File System Version 2(日志文件系统)的缩写,是针对MTD(memory technology device,内存技术设备如Flash、RAM、ROM等以读、擦除为元语的设备)设备的日志型文件系统。其主要应用于NOR FLASH闪存,特点是:可读写、支持数据压缩、提供了崩溃/掉电安全保护、提供“写平衡”支持等。

                                                      • JFFS2结构 首先是节点,JFFS2共支持3种节点,分别为inode、dirent、cleanmarker。三种节点都包含如下图所示的公共节点头。 **图1:**JFFS2 公共节点标头 INODE节点额外包含文件元数据如文件inode号、版本、所属组与用户id、访问时间、该部分数据在整体文件中的偏移、数据长度等,(压缩后的)数据紧随在inode节点之后。DIRENT节点存储文件的目录结构及文件名,额外包含上级目录号pino、文件名大小nsize,其后紧跟着长nsize的字符串即文件名。CLEANMARKER节点只是由于flash介质擦除(全写0)后才能再写而存在,用于标记一段已擦除的空间。 其次是文件组织方式。所有inode号相同的节点均表示同一个文件,由此可看出通过DIRENT存储文件名、INODE存储数据及元数据的方式能够完整实现文件控制块与数据的存储,通过版本号能实现文件的部分修改。目录也被视作一个文件,但不包含对应的INODE数据节点。特别的,JFFS2不存储超级块即super block的信息,超级块是挂载mount命令时由内核扫描建立并存在内存里的;同样的文件索引块也不实际存储,而是运行时生成。 再则是文件基本操作。文件修改时,直接写入修改部分的数据,配上相同的inode号及更新的版本号;文件删除时,将对应DIRENT节点的上级目录号修改为0(实际上也是创建新的高版本节点)。 最后是空间管理。JFFS2是日志文件系统,因此所有数据都顺序写入介质中,直到介质末尾后会从空闲列表中取出空间继续顺序写入。空闲空间通过额外的GC(垃圾回收)机制实现,回收器会从所有节点中找出未被使用(内存里维护有unused标签,如pino为0节点所对应的文件均为unused)的节点,对其物理块进行擦除,随后打上CLEANMARKER记号,并放入空闲列表。 安全措施方面,不论是公共节点头的头部CRC还是INODE中的数据CRC都对数据完整性进行了校验,INODE中的存取控制信息提供了权限上的保护。

                                                    • 总结

                                                      • 此次案例分析获益良多,一方面加强了对课内知识的掌握,看到了众多概念的实际实现,厘清了许多模糊的概念;另一方面也学习了许多扩展知识,例如了解了一部分OHOS系统源码的架构,了解了JFFS2这一文件系统。 分析过程中也遇到过一些问题。起初选择OHOS作为分析对象即是因为此前看到它的官方文档较为完善,然而撰写分析过程中发现,其假设读者对操作系统有过全面的认识,如堆内存、物理内存、虚拟内存三节都有写到内存分配而未说明其间的层次关系,参看其他综述介绍后我才明白首先是申请虚拟内存、其下分配物理内存、最后申请到的内存通过堆内存管理。其二是文档仅对基本原理泛泛而谈,具体如进程PCB实现、KIdle进程、如何通过MMU管理内存映射、页表实现等并未详述,JFFS2文件系统找不到其结构的详细介绍,这部分我通过阅读源码了解其大致实现后总结得出。

                                                      • 引用

                                                      OS case study
                                                      https://astro-pure.js.org/blog/OS%20case%20study
                                                      Author rubbishzyc
                                                      Published at April 8, 2023